package com.yugao.fintech.antelope.statistic.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.IService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yugao.fintech.antelope.base.security.utils.SecurityUtils;
import com.yugao.fintech.antelope.statistic.api.constant.StatisticCons;
import com.yugao.fintech.antelope.statistic.mapper.StatLoginMapper;
import com.yugao.fintech.antelope.statistic.model.entity.StatLogin;
import com.yugao.fintech.antelope.statistic.model.rqrs.StatLoginListReq;
import com.yugao.fintech.antelope.statistic.service.StatLoginService;
import com.yugao.fintech.framework.assistant.utils.exception.BizException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 统计登录与登出数据
 */
@Slf4j
@Service
public class StatLoginServiceImpl extends ServiceImpl<StatLoginMapper, StatLogin> implements IService<StatLogin>, StatLoginService {
    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public List<StatLogin> listStatLogin(StatLoginListReq req) {
        Long tenantId = SecurityUtils.getLoginUser().getTenant().getTenantId();
        // 先从缓存中获取今天的访问量并更新到数据库中, 然后再查询
        String date = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd"));
        String loginKey = String.format(StatisticCons.LOGIN_LOGOUT_KEY, tenantId, date, true);
        String logoutKey = String.format(StatisticCons.LOGIN_LOGOUT_KEY, tenantId, date, false);

        String loginCount = Optional.ofNullable(stringRedisTemplate.opsForValue().get(loginKey)).orElse("0");
        String logoutCount = Optional.ofNullable(stringRedisTemplate.opsForValue().get(logoutKey)).orElse("0");

        StatLogin entity = new StatLogin();
        entity.setLoginCount(Long.valueOf(loginCount)).setLogoutCount(Long.valueOf(logoutCount)).setStatDate(LocalDate.now());
        // 保存或者更新
        LambdaUpdateWrapper<StatLogin> luw = StatLogin.luw().eq(StatLogin::getStatDate, LocalDate.now());
        this.saveOrUpdate(entity, luw);

        // 查询指定时间返回的天数, 不够指定的天数进行补齐
        LocalDate startDate = LocalDate.now().plusDays(req.getDays() * -1L);
        LocalDate endDate = LocalDate.now();
        LambdaQueryWrapper<StatLogin> lqw = StatLogin.lqw()
                .le(StatLogin::getStatDate, endDate).ge(StatLogin::getStatDate, startDate)
                .orderByAsc(StatLogin::getStatDate);
        List<StatLogin> list = this.list(lqw);
        Map<String, StatLogin> queryMap = list.stream()
                .collect(Collectors.toMap(e -> e.getStatDate().format(DateTimeFormatter.ofPattern("yyyyMMdd")), Function.identity()));

        // 对于没有数据的某天, 补0操作
        Map<String, StatLogin> statMap = new TreeMap<>();
        // 往前推一天, 目的是要把今天的也包含在内
        endDate = endDate.plusDays(1);
        while (startDate.isBefore(endDate)) {
            String key = startDate.format(DateTimeFormatter.ofPattern("yyyyMMdd"));
            StatLogin stat = queryMap.get(key);
            if (Objects.isNull(stat)) {
                statMap.put(key, new StatLogin().setStatDate(startDate).setLoginCount(0L).setLogoutCount(0L));
            } else {
                statMap.put(key, stat);
            }
            startDate = startDate.plusDays(1L);
        }

        return new ArrayList<>(statMap.values());
    }

    @Override
    public boolean deleteStatLogin(Long beforeDays) {
        if (beforeDays <= 0) {
            throw new BizException("beforeDays必须 > 0");
        }
        LocalDate startDate = LocalDate.now().plusDays(beforeDays * -1L);
        return this.remove(StatLogin.lqw().le(StatLogin::getStatDate, startDate));
    }

    @Override
    public void saveOrUpdateStatLogin(StatLogin req) {
        LambdaUpdateWrapper<StatLogin> luw = StatLogin.luw().eq(StatLogin::getStatDate, LocalDate.now());
        this.saveOrUpdate(req, luw);
    }


}
